﻿using System;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Collections.Generic;
using System.Linq;
using ServerConsoleTest;
using Communication;
using User;

namespace TCPServer
{
    class Server
    {
        //private const String IPString = "127.0.0.1";// real ip "134.225.180.164"
        private const int PORT = 3000;
        private const int MAX_NUMBER_OF_CLIENTS = 100;

        public int GetNumebrOfClients()
        {
            //this includes inactive,unknown users
            return users.Count;
        }

        private AsyncReceiver receiver;
        private List<UserData> users = new List<UserData>();

        public Server(IPEndPoint endpoint)
        {
            receiver = new AsyncReceiver(endpoint);
            receiver.newMessage += new AsyncReceiver.TickHandler(MessageReceived);
        }

        private void MessageReceived(AsyncReceiver res, MessageEvent msg)
        {
            string message = msg.Message;
            Console.WriteLine("Message received:" + message);
            int usernameStart = message.IndexOf("<u>") + "<u>".Length;
            int usernameEnd = message.IndexOf("<p>");

            if (usernameStart < 0 || usernameEnd < 0) return;

            string username = message.Substring(usernameStart, usernameEnd - usernameStart);
            Console.WriteLine("Username: " + username);

            int userIndex = users.FindIndex(us => us.Username.Equals(username)); // -1 if not found

            int passwordStart = message.IndexOf("<p>") + "<p>".Length;
            int portIndex = message.IndexOf("<port>");
            int messageIndex = message.IndexOf("<m>");

            int passwordEnd;
            if (portIndex < 0 && messageIndex < 0) return;
            if (portIndex < 0 || messageIndex < 0) passwordEnd = Math.Max(portIndex, messageIndex);
            else passwordEnd = Math.Min(portIndex, messageIndex);

            string password = message.Substring(passwordStart, passwordEnd - passwordStart);
            Console.WriteLine("Password: " + password);
            //select username, check password
            
            if (message.StartsWith("<aut>")) // wants to authenticate and no user as before was found;
            {
                // in authentication mode
                // get port
                if (portIndex < 0) return;
                int portStart = portIndex + "<port>".Length;
                int port = int.Parse(message.Substring(portStart, messageIndex - portStart));
                Console.WriteLine("Port: " + port);
                Console.WriteLine("IPAddress: " + msg.IpAddress);
                IPEndPoint currentEndPoint = new IPEndPoint(msg.IpAddress, port);
                int ipportIndex = users.FindIndex(us => us.EndPoint.Equals(currentEndPoint)); // -1 if not found

                if (ipportIndex == -1)
                {
                    //IP/port not found. Create new user
                    CreateNewUser(username, password, currentEndPoint);
                }
                else
                {
                    //IP/port found. 
                    if (userIndex >= 0 && userIndex == ipportIndex) //for optimisation userindex>=0 can be removed, as ipport>=0 asn they equal
                    {
                        //user found and corresponds to ip
                        users[userIndex].Change(username, password);
                        SendValidityCheck(users[ipportIndex]);
                    }
                    else
                    {
                        //User was logged from different PC. create (user/pass) link to current IP
                        //or username not found but IP is, so try to link both.
                        users[ipportIndex].Change(username,password);
                        SendValidityCheck(users[ipportIndex]);
                        //Remove link to old PC
                    }
                }
            }
            else
            {
                //doesnt start with <aut>
                if (userIndex >= 0)
                {
                    //user found
                    if (users[userIndex].IsValid)
                    {
                        int messaageStart = message.IndexOf("<m>") + "<m>".Length;
                        int messageEnd = message.LastIndexOf("<EOF>");
                        string messageText = message.Substring(messaageStart, messageEnd - messaageStart);
                        Console.WriteLine("Message: " + messageText);
                        users[userIndex].Send(messageText + "<EOF>");
                    }
                    else
                    {
                        users[userIndex].Send("User Not valid. Recconect.<EOF>");
                    }
                }
            }
        }

        private void CreateNewUser(string username, string password, IPEndPoint currentEndPoint)
        {
            UserData newUser = new UserData(username, password, currentEndPoint);
            users.Add(newUser);
            SendValidityCheck(newUser);
        }

        private static void SendValidityCheck(UserData newUser)
        {
            if (newUser.IsValid)
            {
                newUser.Send("<true><EOF>");
            }
            else
            {
                newUser.Send("User/password is wrong.<EOF>");
            }
        }

        public void Send(string username, string message)
        {
            int userIndex = users.FindIndex(x => x.Username.Equals(username)); // -1 if not found
            Send(userIndex, message);
        }

        public void Send(int userIndex, string message)
        {
            if (userIndex < 0 || userIndex > users.Count)
            {
                Console.WriteLine("Unknown username index."); 
                return;
            }
            users[userIndex].Send(message + "<EOF>");

        }

        public void SendToAllClients(string message)
        {
            if (users.Count > 0)
            {
                for (int i = 0; i < users.Count; i++)
                {
                    Send(i, "Global message: " + message);
                }
            }

        }
    }
}